import { API_BASE_URL, API_TOKEN } from './config';

export interface EventModel {
  id: string;
  type: string;
  owner: string;
  actor: string;
  created_at: string;
  headers: Record<string, string>;
  content: string;
  attachments: string[];
}

export interface TaskModel {
  id: string;
  title: string;
  description: string;
  status: string;
  created_at: string;
  actor: string;
  assignees: string[];
}

export interface TaskStatusResponse {
  id: string;
  status: string;
}

export interface NotificationModel {
  type: string;
  actor: string;
  created_at: string;
  summary: string;
}

export interface MentionModel {
  id: string;
  type: string;
  actor: string;
  created_at: string;
  snippet: string;
}

export interface AttachmentListing {
  event_id: string;
  files: string[];
}

export interface AccountModel {
  id: string;
  display_name: string;
  type: string;
  created_at: string;
}

export type CommandAction = () => void | Promise<void>;

export class AwApiClient {
  private baseUrl: string;
  private token?: string;

  constructor(baseUrl: string = API_BASE_URL, token: string | undefined = API_TOKEN) {
    this.baseUrl = baseUrl.replace(/\/$/, '');
    this.token = token;
  }

  setAuthToken(token?: string) {
    this.token = token;
  }

  async fetchFeed(owner: string, params: Record<string, string | number | undefined> = {}): Promise<EventModel[]> {
    const qs = new URLSearchParams();
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined && value !== null && value !== '') {
        qs.append(key, String(value));
      }
    });
    const url = this.withQuery(`/api/v1/owners/${owner}/feed`, qs);
    return this.get<EventModel[]>(url);
  }

  async fetchEvent(owner: string, eventId: string) {
    return this.get<EventModel>(`/api/v1/owners/${owner}/events/${eventId}`);
  }

  async createPost(owner: string, actor: string, message: string, options: {
    tags?: string[];
    mentions?: string[];
    files?: File[];
  } = {}): Promise<EventModel> {
    const form = new FormData();
    form.append('actor', actor);
    form.append('message', message);
    if (options.tags && options.tags.length) {
      form.append('tags', options.tags.join(','));
    }
    if (options.mentions && options.mentions.length) {
      form.append('mentions', options.mentions.join(','));
    }
    if (options.files) {
      options.files.forEach(file => form.append('files', file));
    }
    return this.postForm<EventModel>(`/api/v1/owners/${owner}/posts`, form);
  }

  async createComment(owner: string, actor: string, postId: string, message: string): Promise<EventModel> {
    const form = new FormData();
    form.append('actor', actor);
    form.append('post_id', postId);
    form.append('message', message);
    return this.postForm<EventModel>(`/api/v1/owners/${owner}/comments`, form);
  }

  async fetchTasks(owner: string, params: { assignee?: string; status?: string } = {}): Promise<TaskModel[]> {
    const qs = new URLSearchParams();
    if (params.assignee) qs.append('assignee', params.assignee);
    if (params.status) qs.append('status', params.status);
    return this.get<TaskModel[]>(this.withQuery(`/api/v1/owners/${owner}/tasks`, qs));
  }

  async createTask(owner: string, actor: string, title: string, description: string, assignees: string[]): Promise<EventModel> {
    const form = new FormData();
    form.append('actor', actor);
    form.append('title', title);
    form.append('description', description);
    if (assignees.length) {
      form.append('assignees', assignees.join(','));
    }
    return this.postForm<EventModel>(`/api/v1/owners/${owner}/tasks`, form);
  }

  async updateTaskStatus(owner: string, taskId: string, actor: string, status: string): Promise<TaskStatusResponse> {
    const form = new FormData();
    form.append('actor', actor);
    form.append('status', status);
    return this.postForm<TaskStatusResponse>(`/api/v1/owners/${owner}/tasks/${taskId}/status`, form);
  }

  async fetchNotifications(owner: string, target?: string, lastHours = 24): Promise<NotificationModel[]> {
    const qs = new URLSearchParams();
    qs.append('last_hours', String(lastHours));
    if (target) qs.append('target', target);
    return this.get<NotificationModel[]>(this.withQuery(`/api/v1/owners/${owner}/notifications`, qs));
  }

  async fetchMentions(owner: string, target?: string): Promise<MentionModel[]> {
    const qs = new URLSearchParams();
    if (target) qs.append('target', target);
    return this.get<MentionModel[]>(this.withQuery(`/api/v1/owners/${owner}/mentions`, qs));
  }

  async search(owner: string, query: string, actors?: string[]): Promise<EventModel[]> {
    const qs = new URLSearchParams({ owner, query });
    if (actors && actors.length) {
      qs.append('actors', actors.join(','));
    }
    return this.get<EventModel[]>(this.withQuery('/api/v1/search', qs));
  }

  async listAccounts(): Promise<AccountModel[]> {
    return this.get<AccountModel[]>('/api/v1/accounts');
  }

  subscribeFeed(owner: string, onEvent: (payload: unknown) => void): () => void {
    const wsUrl = this.baseUrl.replace(/^http/, 'ws') + `/ws/owners/${owner}/feed`;
    const socket = new WebSocket(wsUrl);
    socket.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        onEvent(data);
      } catch (err) {
        console.warn('feed payload parse error', err);
      }
    };
    return () => {
      socket.close();
    };
  }

  private async get<T>(path: string): Promise<T> {
    const res = await fetch(this.absolute(path), {
      headers: this.headers()
    });
    if (!res.ok) {
      throw new Error(`GET ${path} ${res.status}`);
    }
    return res.json() as Promise<T>;
  }

  private async postForm<T>(path: string, body: FormData): Promise<T> {
    const res = await fetch(this.absolute(path), {
      method: 'POST',
      headers: this.headers(false),
      body
    });
    if (!res.ok) {
      throw new Error(`POST ${path} ${res.status}`);
    }
    return res.json() as Promise<T>;
  }

  private headers(json = true): HeadersInit {
    const headers: HeadersInit = {};
    if (json) headers['Accept'] = 'application/json';
    if (this.token) {
      headers['X-AW-Token'] = this.token;
    }
    return headers;
  }

  private absolute(path: string) {
    if (path.startsWith('http')) return path;
    return `${this.baseUrl}${path}`;
  }

  private withQuery(path: string, qs: URLSearchParams) {
    const query = qs.toString();
    return query ? `${path}?${query}` : path;
  }
}

export const apiClient = new AwApiClient();
